/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package service;

import com.openbrew.openbrewrf.BoilHopNotes;
import com.openbrew.openbrewrf.BrewNotes;
import com.openbrew.openbrewrf.Brews;
import com.openbrew.openbrewrf.Customer;
import com.openbrew.openbrewrf.FinishedGoodsInventory;
import com.openbrew.openbrewrf.Firkins;
import com.openbrew.openbrewrf.LegacySales;
import com.openbrew.openbrewrf.LossLog;
import com.openbrew.openbrewrf.MaltBill;
import com.openbrew.openbrewrf.MaltNotes;
import com.openbrew.openbrewrf.Movement;
import com.openbrew.openbrewrf.OpReport;
import com.openbrew.openbrewrf.PackageType;
import com.openbrew.openbrewrf.Recipes;
import com.openbrew.openbrewrf.ReturnedBeer;
import com.openbrew.openbrewrf.Sales;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

/**
 *
 * @author chapmand
 */
@Stateless
@Path("com.openbrew.openbrewrf.opreport")
public class OpReportREST {
    @PersistenceContext(unitName = "com.openBrew_openBrewRF_war_1.0PU")
    private EntityManager em;
    
    public OpReportREST() {
    }
    
    private List<LossLog> losses = new ArrayList<LossLog>();
    private List<Sales> sales = new ArrayList<Sales>();
    private List<ReturnedBeer> returns = new ArrayList<ReturnedBeer>();
    private List<Firkins> firkins = new ArrayList<Firkins>();
    private List<Movement> mvmts = new ArrayList<Movement>();
    private List<Brews> brews = new ArrayList<Brews>();
    private List<MovementTables> movedBeforeStart = new ArrayList<MovementTables>();
    private List<MovementTables> movedBeforeFinish = new ArrayList<MovementTables>();
    final Map<MaltType, Float> fermentableTotals = new HashMap<MaltType, Float>();
    private int trid = 14;
    
    //Set a class to organize all of the movement data.
    private class MovementTables{
        private int movementBrewID;
        private double lagerVolume = 0; //volume of movement from state 5 to 10
        private double conditioningVolume = 0; //volume of movement from state 10 to 15
        private double keggingVolume = 0; //volume of beer moved into kegs.
        private double bottlingVolume = 0; //volume of beer moved into bottles.
        private double barrelVolume = 0; //volume of beer moved into barrels.
        private double barrelConditioningVolume = 0; //volume of beer moved from barrels to bcond.
        private double firkinVolume = 0; //volume of beer moved into firkins.
        private double volumeMovedOutOfLager = 0; //volume of movement from state 10
        private double volumeLeftInCellar = 0;
        private double volumeLeftInBarrel = 0;

        public void setBrewID(final int id){
            this.movementBrewID = id;
        }

        public int getBrewID(){
            return this.movementBrewID;
        }

        public void setLagerVol(final double vol){
            this.lagerVolume = vol;
        }

        public double getLagerVol(){
            return this.lagerVolume;
        }

        public void setConditioningVolume(final double vol){
            this.conditioningVolume = vol;
        }

        public double getConditioningVolume(){
            return this.conditioningVolume;
        }

        public void setKeggingVolume(final double vol){
            this.keggingVolume = vol;
        }

        public double getKeggingVolume(){
            return this.keggingVolume;
        }

        public void setBottlingVolume(final double vol){
            this.bottlingVolume = vol;
        }

        public double getBottlingVolume(){
            return this.bottlingVolume;
        }

        public void setBarrelVolume(final double vol){
            this.barrelVolume = vol;
        }

        public double getBarrelVolume(){
            return this.barrelVolume;
        }

        public void setBarrelConditioningVolume(final double vol){
            this.barrelConditioningVolume = vol;
        }

        public double getBarrelConditioningVolume(){
            return this.barrelConditioningVolume;
        }

        public void setFirkinVolume(final double vol){
            this.firkinVolume = vol;
        }

        public double getFirkinVolume(){
            return this.firkinVolume;
        }

        public void setVolumeLeftInCellar(final double vol){
            this.volumeLeftInCellar = vol;
        }

        public double getVolumeLeftInCellar(){
            return this.volumeLeftInCellar;
        }

        public void setVolumeLeftInBarrel(final double vol){
            this.volumeLeftInBarrel = vol;
        }

        public double getVolumeLeftInBarrel(){
            return this.volumeLeftInBarrel;
        }

        public void setVolumeMovedOutOfLager(final double vol){
            this.volumeMovedOutOfLager = vol;
        }

        public double getVolumeMovedOutOfLager(){
            return this.volumeMovedOutOfLager;
        }
    }
    
    public enum MaltType {

	OATS, WHEAT, HONEY, MOLASSES, BARLEY, OTHER;
	public static MaltType getMaltTypeFromID(final int id) {

		switch (id) {
		case 6:
			return WHEAT;
		case 25:// flaked oats
		case 42:// malted oats
			return OATS;
		case 0:// not set
		case 8:// rye
		case 40:// flaked corn
		case 44:// brown sugar
		case 45:// white sugar
		case 47:// maple syrup
		case 49:// rice hulls
			return OTHER;
		case 46:
			return HONEY;
		case 48:
			return MOLASSES;
		default:
			return BARLEY;
		}
	}
    }
    
        public int getBrewsBeforeDate(java.util.Date sDate){
            int brewID = 0;
            for(Movement move : mvmts){
                if(move.getMovementDate().before(sDate)){
                    if(move.getMovementBrewID() > brewID){
                        brewID = move.getMovementBrewID();
                    }
                }
            }
            return brewID;
        }
    
        public int getBrewsBeforeorEqualDate(java.util.Date sDate){
            int brewID = 0;
            for(Movement move : mvmts){
                if((move.getMovementDate().before(sDate)||(move.getMovementDate().equals(sDate)))){
                    if(move.getMovementBrewID() > brewID){
                        brewID = move.getMovementBrewID();
                    }
                }
            }
            return brewID;
        }
        
        public void createMovementBeforeStartTable(java.util.Date sDate){
            movedBeforeStart.removeAll(movedBeforeStart);
            final int numberOfBrews = getBrewsBeforeDate(sDate);
            for(int i=1; i<=numberOfBrews; i++){
                MovementTables mTbl = new MovementTables();
                mTbl.setBrewID(i);
                for(Movement move : mvmts){
                    if((move.getMovementDate().before(sDate))&&(move.getMovementBrewID()==i)){
                        //If the date is before the start of the report 
                        //and the brew is the same as currently selected, then we need to add info.
                        if(move.getMovementStateID()==10){
                            mTbl.setLagerVol(mTbl.getLagerVol()+(double)move.getMovementVolume());
                        } else if(move.getMovementStateID()==15){
                            mTbl.setConditioningVolume(mTbl.getConditioningVolume()+(double)move.getMovementVolume());
                        } else if(move.getMovementStateID()==20){
                            mTbl.setKeggingVolume(mTbl.getKeggingVolume()+(double)move.getMovementVolume());
                        } else if(move.getMovementStateID()==21){
                            mTbl.setBottlingVolume(mTbl.getBottlingVolume()+(double)move.getMovementVolume());
                        } else if(move.getMovementStateID()==25){
                            mTbl.setBarrelVolume(mTbl.getBarrelVolume()+(double)move.getMovementVolume());
                        } else if(move.getMovementStateID()==16){
                            mTbl.setBarrelConditioningVolume(mTbl.getBarrelConditioningVolume()+(double)move.getMovementVolume());
                        } else if(move.getMovementStateID()==22){
                            mTbl.setFirkinVolume(mTbl.getFirkinVolume()+(double)move.getMovementVolume());
                        }
                        
                        if(move.getMovementOldState()==10){
                            mTbl.setVolumeMovedOutOfLager(mTbl.getVolumeMovedOutOfLager() + (double)move.getMovementVolume());
                        }
                    }
                }
                movedBeforeStart.add(mTbl);
            }
        }
        
        public void createMovementBeforeFinishTable(java.util.Date fDate){
            movedBeforeFinish.removeAll(movedBeforeFinish);
            final int numberOfBrews = getBrewsBeforeorEqualDate(fDate);
            for(int i=1; i<=numberOfBrews; i++){
                MovementTables mTbl = new MovementTables();
                mTbl.setBrewID(i);
                for(Movement move : mvmts){
                    if((move.getMovementDate().before(fDate) || move.getMovementDate().equals(fDate))
                            &&(move.getMovementBrewID()==i)){
                        //If the date is before the start of the report 
                        //and the brew is the same as currently selected, then we need to add info.
                        if(move.getMovementStateID()==10){
                            mTbl.setLagerVol(mTbl.getLagerVol()+(double)move.getMovementVolume());
                        } else if(move.getMovementStateID()==15){
                            mTbl.setConditioningVolume(mTbl.getConditioningVolume()+(double)move.getMovementVolume());
                        } else if(move.getMovementStateID()==20){
                            mTbl.setKeggingVolume(mTbl.getKeggingVolume()+(double)move.getMovementVolume());
                        } else if(move.getMovementStateID()==21){
                            mTbl.setBottlingVolume(mTbl.getBottlingVolume()+(double)move.getMovementVolume());
                        } else if(move.getMovementStateID()==25){
                            mTbl.setBarrelVolume(mTbl.getBarrelVolume()+(double)move.getMovementVolume());
                        } else if(move.getMovementStateID()==16){
                            mTbl.setBarrelConditioningVolume(mTbl.getBarrelConditioningVolume()+(double)move.getMovementVolume());
                        } else if(move.getMovementStateID()==22){
                            mTbl.setFirkinVolume(mTbl.getFirkinVolume()+(double)move.getMovementVolume());
                        }
                        
                        if(move.getMovementOldState()==10){
                            mTbl.setVolumeMovedOutOfLager(mTbl.getVolumeMovedOutOfLager() + (double)move.getMovementVolume());
                        }
                    }
                }
                movedBeforeFinish.add(mTbl);
            }
        }
        
        public void createMovementTables(java.util.Date sDate, java.util.Date fDate){
            createMovementBeforeStartTable(sDate);
            createMovementBeforeFinishTable(fDate);
        }
        
        public double totalCellar(final List<MovementTables> array){
            double cellarTot = 0;
            for(MovementTables mt : array){
                cellarTot += mt.getVolumeLeftInCellar();
            }
            return cellarTot;
        }
        
        public double totalBarrel(final List<MovementTables> array){
            double cellarTot = 0;
            for(MovementTables mt : array){
                cellarTot += mt.getBarrelVolume();
            }
            return cellarTot;
        }
        
        public double totalKegs(final List<MovementTables> array){
            double kegTot = 0;
            for(MovementTables mt : array){
                kegTot += mt.getKeggingVolume();
            }
            return kegTot;
        }
        
        public double totalBottles(final List<MovementTables> array){
            double btlTot = 0;
            for(MovementTables mt : array){
                btlTot += mt.getBottlingVolume();
            }
            return btlTot;
        }
        
        public double totalKegVolume(Sales sale){
            double kegVolume = 0;
            if(sale.getLegacySalesCollection()!=null){
                for(LegacySales ls : sale.getLegacySalesCollection()){
                    kegVolume += (double)ls.getNumHalf()/ 2;
                    kegVolume += (double)ls.getNumQtr() / 4;
                    kegVolume += (double)ls.getNumSix() / 6;
                }
            }
            if(sale.getSalesFGIID()!=null){
                FinishedGoodsInventory fgi = sale.getSalesFGIID();
                PackageType pkg = fgi.getFGIPackageID();
                if(pkg.isServingVessel()){
                    kegVolume += (double)sale.getServingTankVolume();
                } else {
                    if(pkg.getPackageTypeVol()>=0.15){
                        kegVolume += (double)sale.getSalesFGIQty() * (double)pkg.getPackageTypeVol() * (double)fgi.getFGIPackageID().getNumItems();
                    }
                }
            }
            return kegVolume;
        }
        
        public double totalBottleVolume(Sales sale){
            double btlVolume = 0;
            if(sale.getLegacySalesCollection()!=null){
                for(LegacySales ls : sale.getLegacySalesCollection()){
                    btlVolume += (double)ls.getNumTT() * (double)12 * 0.00554435484;
                }
            }
            if(sale.getSalesFGIID()!=null){
                FinishedGoodsInventory fgi = sale.getSalesFGIID();
                PackageType pkg = fgi.getFGIPackageID();
                if(pkg.getPackageTypeVol()<0.15){
                    btlVolume += (double)sale.getSalesFGIQty() * (double)pkg.getPackageTypeVol() * (double)fgi.getFGIPackageID().getNumItems();
                }
            }
            return btlVolume;
        }
        
        public double totalKegVolume(ReturnedBeer sale){
            double kegVolume = 0;
            FinishedGoodsInventory fgi = sale.getRetFGIID();
            PackageType pkg = fgi.getFGIPackageID();
            if(pkg.getPackageTypeVol()>=0.15){
                kegVolume += (double)sale.getRetFGIQty() * (double)pkg.getPackageTypeVol() * (double)fgi.getFGIPackageID().getNumItems();
            }
            return kegVolume;
        }
        
        public double totalBottleVolume(ReturnedBeer sale){
            double btlVolume = 0;
            FinishedGoodsInventory fgi = sale.getRetFGIID();
            PackageType pkg = fgi.getFGIPackageID();
            if(pkg.getPackageTypeVol()<0.15){
                btlVolume += (double)sale.getRetFGIQty() * (double)pkg.getPackageTypeVol() * (double)fgi.getFGIPackageID().getNumItems();
            }
            return btlVolume;
        }
        
        public double getKegSalesBeforeStartDate(java.util.Date sDate){
            double kegs = 0;
            for(final Sales sale : sales){
                if(sale.getSDate().before(sDate)){
                    kegs += totalKegVolume(sale);
                }
            }
            return kegs;
        }
        
        public double getBottleSalesBeforeStartDate(java.util.Date sDate){
            double bottle = 0;
            for(final Sales sale : sales){
                if(sale.getSDate().before(sDate)){
                    bottle += totalBottleVolume(sale);
                }
            }
            return bottle;
        }
        
        public double getDistCoBottleSalesBetweenDates(java.util.Date sDate, java.util.Date fDate){
            double bottles = 0;
            for(final Sales sale : sales){
                if((sale.getSDate().before(fDate)&&sale.getSDate().after(sDate)) 
                        || sale.getSDate().equals(fDate) || sale.getSDate().equals(sDate) ){
                    if(sale.getCustID()!=null){
                        Customer cust = sale.getCustID();
                        if(cust.getCdist()){
                            bottles += totalBottleVolume(sale);
                        }
                    }
                }
            }
            return bottles;
        }
        
        public double getDistCoKegSalesBetweenDates(java.util.Date sDate, java.util.Date fDate){
            double kegs = 0;
            for(final Sales sale : sales){
                if((sale.getSDate().before(fDate)&&sale.getSDate().after(sDate)) 
                        || sale.getSDate().equals(fDate) || sale.getSDate().equals(sDate) ){
                    if(sale.getCustID()!= null){
                        Customer cust = sale.getCustID();
                        if(cust.getCdist()){
                            kegs += totalKegVolume(sale);
                        }
                    }
                }
            }
            return kegs;
        }
        
        public double getKegSalesBeforeFinishDate(java.util.Date fDate){
            double kegs = 0;
            for(final Sales sale : sales){
                if(sale.getSDate().before(fDate) || sale.getSDate().equals(fDate)){
                    kegs += totalKegVolume(sale);
                }
            }
            return kegs;
        }
        
        public double getBottleSalesBeforeFinishDate(java.util.Date fDate){
            double bottle = 0;
            for(final Sales sale : sales){
                if(sale.getSDate().before(fDate) || sale.getSDate().equals(fDate)){
                    bottle += totalBottleVolume(sale);
                }
            }
            return bottle;
        }
        
        public double getCellarLossBetweenDates(java.util.Date sDate, java.util.Date fDate){
            double lossVolume = 0;
            for(final LossLog loss : losses){
                if((loss.getLDate().after(sDate)&&loss.getLDate().before(fDate))
                        || loss.getLDate().equals(sDate) || loss.getLDate().equals(fDate)){
                    lossVolume += (double)loss.getLCellar();
                    lossVolume += (double)loss.getLAngelShare();
                }
            }
            return lossVolume;
        }
        
        public double getKegLossBetweenDates(java.util.Date sDate, java.util.Date fDate){
            double kegLoss = 0;
            for(final LossLog loss : losses){
                if((loss.getLDate().after(sDate)&&loss.getLDate().before(fDate))
                        || loss.getLDate().equals(sDate) || loss.getLDate().equals(fDate)){
                    kegLoss += (double)loss.getLKeg();
                }
            }
            return kegLoss;
        }
        
        public double getBottleLossBetweenDates(java.util.Date sDate, java.util.Date fDate){
            double bottleLoss = 0;
            for(final LossLog loss : losses){
                if((loss.getLDate().after(sDate)&&loss.getLDate().before(fDate))
                        || loss.getLDate().equals(sDate) || loss.getLDate().equals(fDate)){
                    bottleLoss += (double)loss.getLBtl();
                }
            }
            return bottleLoss;
        }
        
        public double getKegLossBeforeStart(java.util.Date sDate){
            double kegLoss = 0;
            for(final LossLog loss : losses){
                if(loss.getLDate().before(sDate)){
                    kegLoss += (double)loss.getLKeg();
                }
            }
            return kegLoss;
        }
        
        public double getBottleLossBeforeStart(java.util.Date sDate){
            double btlLoss = 0;
            for(final LossLog loss : losses){
                if(loss.getLDate().before(sDate)){
                    btlLoss += (double)loss.getLBtl();
                }
            }
            return btlLoss;
        }
        
        public double getKegLossBeforeFinish(java.util.Date fDate){
            double kegLoss = 0;
            for(final LossLog loss : losses){
                if(loss.getLDate().before(fDate) || loss.getLDate().equals(fDate)){
                    kegLoss += (double)loss.getLKeg();
                }
            }
            return kegLoss;
        }
        
        public double getBottleLossBeforeFinish(java.util.Date fDate){
            double btlLoss = 0;
            for(final LossLog loss : losses){
                if(loss.getLDate().before(fDate) || loss.getLDate().equals(fDate)){
                    btlLoss += (double)loss.getLBtl();
                }
            }
            return btlLoss;
        }
        
        public double getKegSalesBetweenDatesForOutsideCustomers(java.util.Date sDate, java.util.Date fDate){
            double kegs = 0;
            for(final Sales sale : sales){
                if((sale.getSDate().before(fDate)&&(sale.getSDate().after(sDate))) 
                        || sale.getSDate().equals(sDate) || sale.getSDate().equals(fDate)){
                    if(sale.getCustID()!=null){
                        if(sale.getCustID().getCId() != trid){
                            kegs += totalKegVolume(sale);
                        }
                    } else {
                        kegs += totalKegVolume(sale);
                    }
                }
            }
            return kegs;
        }
        
        public double getBottleSalesBetweenDatesForOutsideCustomers(java.util.Date sDate, java.util.Date fDate){
            double bottle = 0;
            for(final Sales sale : sales){
                if((sale.getSDate().before(fDate)&&(sale.getSDate().after(sDate))) 
                        || sale.getSDate().equals(sDate) || sale.getSDate().equals(fDate)){
                    if(sale.getCustID()!=null){
                        if(sale.getCustID().getCId() != trid){
                            bottle += totalBottleVolume(sale);
                        }
                    } else {
                        bottle += totalBottleVolume(sale);
                    }
                }
            }
            return bottle;
        }
        
        public double getKegSalesBetweenDatesForTaproom(java.util.Date sDate, java.util.Date fDate){
            double kegs = 0;
            for(final Sales sale : sales){
                if((sale.getSDate().before(fDate)&&(sale.getSDate().after(sDate))) 
                        || sale.getSDate().equals(sDate) || sale.getSDate().equals(fDate)){
                    if(sale.getCustID()!=null){
                        if(sale.getCustID().getCId() == trid){
                            kegs += totalKegVolume(sale);
                        }
                    }
                }
            }
            return kegs;
        }
        
        public double getBottleSalesBetweenDatesForTaproom(java.util.Date sDate, java.util.Date fDate){
            double bottle = 0;
            for(final Sales sale : sales){
                if((sale.getSDate().before(fDate)&&(sale.getSDate().after(sDate))) 
                        || sale.getSDate().equals(sDate) || sale.getSDate().equals(fDate)){
                    if(sale.getCustID()!=null){
                        if(sale.getCustID().getCId() == trid){
                            bottle += totalBottleVolume(sale);
                        }
                    }
                }
            }
            return bottle;
        }
        
        public double getKegReturnsPriorToStartDate(java.util.Date sDate){
            double returnVol = 0;
            for(final ReturnedBeer returnItem : returns){
                if(returnItem.getRetDate().before(sDate)){
                    returnVol += totalKegVolume(returnItem);
                }
            }
            return returnVol;
        }
        
        public double getBottleReturnsPriorToStartDate(java.util.Date sDate){
            double returnVol = 0;
            for(final ReturnedBeer returnItem : returns){
                if(returnItem.getRetDate().before(sDate)){
                    returnVol += totalBottleVolume(returnItem);
                }
            }
            return returnVol;
        }
        
        public double getKegReturnsPriorToFinishDate(java.util.Date fDate){
            double returnVol = 0;
            for(final ReturnedBeer returnItem : returns){
                if(returnItem.getRetDate().before(fDate) || returnItem.getRetDate().equals(fDate)){
                    returnVol += totalKegVolume(returnItem);
                }
            }
            return returnVol;
        }
        
        public double getBottleReturnsPriorToFinishDate(java.util.Date fDate){
            double returnVol = 0;
            for(final ReturnedBeer returnItem : returns){
                if(returnItem.getRetDate().before(fDate) || returnItem.getRetDate().equals(fDate)){
                    returnVol += totalBottleVolume(returnItem);
                }
            }
            return returnVol;
        }
        
        public double getKegReturnsBetweenDates(java.util.Date sDate, java.util.Date fDate){
            double returnVol = 0;
            for(final ReturnedBeer returnItem : returns){
                if((returnItem.getRetDate().before(fDate) && returnItem.getRetDate().after(sDate))
                        || returnItem.getRetDate().equals(fDate) || returnItem.getRetDate().equals(sDate)){
                    returnVol += totalKegVolume(returnItem);
                }
            }
            return returnVol;
        }
        
        public double getBottleReturnsBetweenDates(java.util.Date sDate, java.util.Date fDate){
            double returnVol = 0;
            for(final ReturnedBeer returnItem : returns){
                if((returnItem.getRetDate().before(fDate) && returnItem.getRetDate().after(sDate))
                        || returnItem.getRetDate().equals(fDate) || returnItem.getRetDate().equals(sDate)){
                    returnVol += totalBottleVolume(returnItem);
                }
            }
            return returnVol;
        }
        
        public void parseMovedBeforeStart(java.util.Date sDate){
            int numberOfBrews = movedBeforeStart.size();
            for(int i=0; i<numberOfBrews; i++){
                //Calculate volume left in the system.
                MovementTables mt = movedBeforeStart.get(i);
                double volumeLeftInCellar = 0;
                double volumeLeftInBarrel = 0;
                final double twentyPercent = mt.getLagerVol() * 0.20;
                if((mt.getLagerVol()>0)&&(mt.getConditioningVolume()==0)){
                    //beer wasn't moved to conditioning so all volume was from lager.
                    volumeLeftInCellar = mt.getLagerVol();
                } else if(Math.abs(mt.getConditioningVolume()-mt.getLagerVol())<=twentyPercent){
                    //all of the beer was moved from lager to condition
                    volumeLeftInCellar = mt.getConditioningVolume();
                } else {
                    //if we are here, we need to see what total was moved from lager.
                    volumeLeftInCellar = mt.getVolumeMovedOutOfLager();
                }
                
                volumeLeftInCellar = volumeLeftInCellar - mt.getBottlingVolume() 
                        - mt.getKeggingVolume() + mt.getBarrelConditioningVolume() 
                        - mt.getFirkinVolume() - mt.getBarrelVolume();
                volumeLeftInBarrel = mt.getBarrelVolume() - mt.getBarrelConditioningVolume();
                
                for(LossLog loss : losses){
                    if(loss.getLDate().before(sDate)){
                        if(loss.getLBeerID() == mt.getBrewID()){
                            volumeLeftInCellar = volumeLeftInCellar - (double)loss.getLCellar();
                            volumeLeftInBarrel = volumeLeftInBarrel - (double)loss.getLAngelShare();
                        }
                    }
                }
                
                if(volumeLeftInCellar < twentyPercent){
                    volumeLeftInCellar = 0;
                }
                
                movedBeforeStart.get(i).setVolumeLeftInCellar(volumeLeftInCellar);
                movedBeforeStart.get(i).setVolumeLeftInBarrel(volumeLeftInBarrel);  
            }
        }
        
        public void parseMovedBeforeFinish(java.util.Date fDate){
            int numberOfBrews = movedBeforeFinish.size();
            for(int i=0; i<numberOfBrews; i++){
                //Calculate volume left in the system.
                MovementTables mt = movedBeforeFinish.get(i);
                double volumeLeftInCellar = 0;
                double volumeLeftInBarrel = 0;
                final double twentyPercent = mt.getLagerVol() * 0.20;
                if((mt.getLagerVol()>0)&&(mt.getConditioningVolume()==0)){
                    //beer wasn't moved to conditioning so all volume was from lager.
                    volumeLeftInCellar = mt.getLagerVol();
                } else if(Math.abs(mt.getConditioningVolume()-mt.getLagerVol())<=twentyPercent){
                    //all of the beer was moved from lager to condition
                    volumeLeftInCellar = mt.getConditioningVolume();
                } else {
                    //if we are here, we need to see what total was moved from lager.
                    volumeLeftInCellar = mt.getVolumeMovedOutOfLager();
                }
                
                volumeLeftInCellar = volumeLeftInCellar - mt.getBottlingVolume() 
                        - mt.getKeggingVolume() + mt.getBarrelConditioningVolume() 
                        - mt.getFirkinVolume() - mt.getBarrelVolume();
                volumeLeftInBarrel = mt.getBarrelVolume() - mt.getBarrelConditioningVolume();
                
                for(LossLog loss : losses){
                    if(loss.getLDate().before(fDate) || loss.getLDate().equals(fDate)){
                        if(loss.getLBeerID() == mt.getBrewID()){
                            volumeLeftInCellar = volumeLeftInCellar - (double)loss.getLCellar();
                            volumeLeftInBarrel = volumeLeftInBarrel - (double)loss.getLAngelShare();
                        }
                    }
                }
                
                if(volumeLeftInCellar < twentyPercent){
                    volumeLeftInCellar = 0;
                }
                
                movedBeforeFinish.get(i).setVolumeLeftInCellar(volumeLeftInCellar);
                movedBeforeFinish.get(i).setVolumeLeftInBarrel(volumeLeftInBarrel);  
            }
        }
        
        private double calcTotalCellarPriorToStart(){
            final double cellarMovemementPriorToStart = totalCellar(movedBeforeStart);
            final double barrelMovemementPriorToStart = totalBarrel(movedBeforeStart);
            final double totalCellarPriorToStart = cellarMovemementPriorToStart 
                    + barrelMovemementPriorToStart;
            return totalCellarPriorToStart;
        }
        
        private double calcTotalCellarPriorToFinish(){
            final double cellarMovemementPriorToFinish = totalCellar(movedBeforeFinish);
            final double barrelMovemementPriorToFinish = totalBarrel(movedBeforeFinish);
            final double cellarLeftPriorToFinish = cellarMovemementPriorToFinish 
                    + barrelMovemementPriorToFinish;
            return cellarLeftPriorToFinish;
        }
        
        private double calcTotalKegsPriorToStart(java.util.Date sDate){
            final double kegMovementPriorToStart = totalKegs(movedBeforeStart);
            final double kegSalesPriorToStart = getKegSalesBeforeStartDate(sDate);
            final double kegLossBeforeStartDate = getKegLossBeforeStart(sDate);
            final double kegReturnedPriorToStart = getKegReturnsPriorToStartDate(sDate);
            final double firkinSalesPriorToStart = calcFirkinsSoldPriorToStart(sDate);
            final double kegsRemainingOnStartDate = kegMovementPriorToStart 
                    - kegSalesPriorToStart - kegLossBeforeStartDate 
                    + kegReturnedPriorToStart - firkinSalesPriorToStart;
            return kegsRemainingOnStartDate;
        }
        
        private double calcTotalBottlesPriorToStart(java.util.Date sDate){
            final double bottleMovementPriorToStart = totalBottles(movedBeforeStart);
            final double bottleSalesPriorToStart = getBottleSalesBeforeStartDate(sDate);
            final double bottleLossBeforeStartDate = getBottleLossBeforeStart(sDate);
            final double bottleReturnedPriorToStart = getBottleReturnsPriorToStartDate(sDate);
            final double bottlesRemainingOnStartDate = bottleMovementPriorToStart 
                    - bottleSalesPriorToStart - bottleLossBeforeStartDate + bottleReturnedPriorToStart;
            return bottlesRemainingOnStartDate;
        }
        
        private double calcTotalKegsPriorToFinish(java.util.Date fDate){
            final double kegMovementPriorToFinish = totalKegs(movedBeforeFinish);
            final double kegSalesPriorToFinish = getKegSalesBeforeFinishDate(fDate);
            final double kegLossBeforeFinishDate = getKegLossBeforeFinish(fDate);
            final double kegReturnedPriorToFinish = getKegReturnsPriorToFinishDate(fDate);
            final double firkinSalesPriorToFinish = calcFirkinsSoldPriorToFinish(fDate);
            final double kegsRemainingOnFinishDate = kegMovementPriorToFinish 
                    - kegSalesPriorToFinish - kegLossBeforeFinishDate 
                    + kegReturnedPriorToFinish - firkinSalesPriorToFinish;
            return kegsRemainingOnFinishDate;
        }
        
        private double calcTotalBottlesPriorToFinish(java.util.Date fDate){
            final double bottleMovementPriorToFinish = totalBottles(movedBeforeFinish);
            final double bottleSalesPriorToFinish = getBottleSalesBeforeFinishDate(fDate);
            final double bottleLossBeforeFinishDate = getBottleLossBeforeFinish(fDate);
            final double bottleReturnedPriorToFinish = getBottleReturnsPriorToFinishDate(fDate);
            final double bottlesRemainingOnFinishDate = bottleMovementPriorToFinish 
                    - bottleSalesPriorToFinish - bottleLossBeforeFinishDate + bottleReturnedPriorToFinish;
            return bottlesRemainingOnFinishDate;
        }
        
        private double calcFirkinsSoldPriorToStart(java.util.Date sDate){
            double firkVolume = 0;
            for(final Firkins firk : firkins){
                if(firk.getFirkSold()){
                    if(firk.getFirkSalesDate()!=null){
                        if(firk.getFirkSalesDate().before(sDate)){
                            firkVolume += (double)firk.getFirkVolume() / 31;
                        }
                    }
                }
            }
            return firkVolume;
        }
        
        private double calcFirkinsSoldPriorToFinish(java.util.Date fDate){
            double firkVolume = 0;
            for(final Firkins firk : firkins){
                if(firk.getFirkSalesDate()!=null){
                    if(firk.getFirkSold()){
                        if(firk.getFirkSalesDate().before(fDate) || firk.getFirkSalesDate().equals(fDate) ){
                            firkVolume += (double)firk.getFirkVolume() / 31;
                        }
                    }
                }
            }
            return firkVolume;
        }
        
        private double getTotalHopsBetweenDates(java.util.Date sDate, java.util.Date fDate){
            double totalHops = 0;
            for(final Brews brew : brews){
                if((brew.getBDate().after(sDate)&&brew.getBDate().before(fDate)) 
                        || brew.getBDate().equals(sDate) || brew.getBDate().equals(fDate)){
                    final Recipes recipe = brew.getRid();
                    addMaltCounts(recipe, fermentableTotals, brew);
                    totalHops += totalHops(brew); 
                }
            }
            return totalHops;
        }
        
        private double totalHops(Brews brew){
            double totalHops = 0;
            final List<BrewNotes> ids = new ArrayList<BrewNotes>();
            ids.add(brew.getBrewNoteID());
            ids.add(brew.getBrewNoteID2());
            ids.add(brew.getBrewNoteID3());
            ids.add(brew.getBrewNoteID4());
            ids.add(brew.getBrewNoteID5());
            ids.add(brew.getBrewNoteID6());
            for(final BrewNotes bn : ids){
                if(bn != null){
                    if(bn.getBoilHopNoteCollection() != null){
                        for(final BoilHopNotes bhn : bn.getBoilHopNoteCollection()){
                            totalHops += bhn.getBoilHopLbs();
                        }
                    }
                }
            }
            return totalHops;
        }

	private void addMaltCounts(final Recipes recipe
                , final Map<MaltType, Float> fermentableTotals, final Brews brew) {
            //recipe.getMName01();
            final List<BrewNotes> ids = new ArrayList<BrewNotes>();
            ids.add(brew.getBrewNoteID());
            ids.add(brew.getBrewNoteID2());
            ids.add(brew.getBrewNoteID3());
            ids.add(brew.getBrewNoteID4());
            ids.add(brew.getBrewNoteID5());
            ids.add(brew.getBrewNoteID6());

            for (final BrewNotes bn : ids) {
                if(bn != null){
                    final List<Float> maltsUsed = new ArrayList<Float>();
                    for(final MaltNotes mn : bn.getMaltNoteCollection()){
                        maltsUsed.add(mn.getMaltNoteLbs());
                    }
                    int x = 0;
                    for(final MaltBill mb : recipe.getMaltBillCollection()){
                        updateFermentableTotals(mb.getMaltID().getMaltID(), maltsUsed.get(x), fermentableTotals);
                        x++;
                    }
                }
            }
	}

	private static void updateFermentableTotals(final int maltId, final float lbs, final Map<MaltType, Float> fermentableTotals) {
		final MaltType type = MaltType.getMaltTypeFromID(maltId);
		Float fermentable = fermentableTotals.get(type);
		fermentable += lbs;
		fermentableTotals.put(type, fermentable);
	}
        
        private void loadReturns(){
            returns.removeAll(returns);
            returns = getEntityManager().createNamedQuery("ReturnedBeer.findAll", ReturnedBeer.class).getResultList();
        }
        
        private void loadSales(){
            sales.removeAll(sales);
            sales = getEntityManager().createNamedQuery("Sales.findAll", Sales.class).getResultList();
        }
        
        private void loadLosses(){
            losses.removeAll(losses);
            losses = getEntityManager().createNamedQuery("LossLog.findAll", LossLog.class).getResultList();
        }
        
        private void loadMovements(){
            mvmts.removeAll(mvmts);
            mvmts = getEntityManager().createNamedQuery("Movement.findAll", Movement.class).getResultList();
        }
    
        private void loadFirkins(){
            firkins.removeAll(firkins);
            firkins = getEntityManager().createNamedQuery("Firkins.findAll", Firkins.class).getResultList();
        }
        
        private void loadBrews(){
            brews.removeAll(brews);
            brews = getEntityManager().createNamedQuery("Brews.findAll", Brews.class).getResultList();
        }

    @GET
    @Path("{id}/{start}/{stop}")
    @Produces(MediaType.APPLICATION_XML)
    public OpReport find(@PathParam("id") Integer id
            , @PathParam("start") Date sDate
            , @PathParam("stop") Date fDate) {
        OpReport or = new OpReport();
        this.trid = id;
        
        loadMovements();
        loadLosses();
        loadSales();
        loadReturns();
        loadFirkins();
        loadBrews();
            
        createMovementTables(sDate, fDate);

        //Parse data groups and set volume variables in each case.
        parseMovedBeforeStart(sDate);
        parseMovedBeforeFinish(fDate);
     
            
        //----------------------
        //--Calculation Part 1--
        //----------------------
        //Calculate movements in cellar prior to start.
        or.setTotCellarPriorToStart(calcTotalCellarPriorToStart());

        //Calculate kegs moved prior to start.
        or.setkegsRemOnStartDate(calcTotalKegsPriorToStart(sDate));

        //Calculate bottles moved prior to start.
        or.setbottlesRemOnStartDate(calcTotalBottlesPriorToStart(sDate));
            
        //----------------------
        //--Calculation Part 2--
        //----------------------
        or.setkegMovePriorToStart(totalKegs(movedBeforeStart));
        or.setkegMoveBeforeFinish(totalKegs(movedBeforeFinish));
        or.setkegsRackBetweenDates(or.getkegMoveBeforeFinish() - or.getkegMovePriorToStart());
            
        or.setbottleMovePriorToStart(totalBottles(movedBeforeStart));
        or.setbottleMoveBeforeFinish(totalBottles(movedBeforeFinish));
        or.setbottlesRackBetweenDates(or.getbottleMoveBeforeFinish() - or.getbottleMovePriorToStart());
            
        //Calculate all losses between dates.
        or.setcelLossBetweenDates(getCellarLossBetweenDates(sDate, fDate));
           
        //----------------------
        //--Calculation Part 3--
        //----------------------
        //Calculate cellar left prior to finish.
        or.setcelLeftPriorToFinish(calcTotalCellarPriorToFinish());

        //Calculate kegs moved prior to start.
        or.setkegsRemOnFinishDate(calcTotalKegsPriorToFinish(fDate));
            
        //Calculate bottles moved prior to start.
        or.setbottlesRemOnFinishDate(calcTotalBottlesPriorToFinish(fDate));
            
        //Calculate movements in cellar prior to start.
        //Calculate the amount fermented based on what is left and what was left.
        or.settotalProdByFermentation(
                or.getcelLeftPriorToFinish() - or.getTotCellarPriorToStart()
                + or.getkegsRackBetweenDates() + or.getbottlesRackBetweenDates() 
                + or.getcelLossBetweenDates());
            
        or.settotalFirkSoldBetweenDates(calcFirkinsSoldPriorToFinish(fDate) 
                - calcFirkinsSoldPriorToStart(sDate));
        
        //----------------------
        //--Calculation Part 4--
        //----------------------
        or.settotalKegOutsideSaleBetweenDates(getKegSalesBetweenDatesForOutsideCustomers(sDate, fDate));
	or.settotalBtlOutsideSaleBetweenDates(getBottleSalesBetweenDatesForOutsideCustomers(sDate, fDate));
        or.settotalKegTapSaleBetweenDates(getKegSalesBetweenDatesForTaproom(sDate, fDate) + (float)or.gettotalFirkSoldBetweenDates());
	or.settotalBtlTapSaleBetweenDates(getBottleSalesBetweenDatesForTaproom(sDate, fDate));
	or.setTotalKegLossBetweenDates(getKegLossBetweenDates(sDate, fDate));
        or.setTotalBottleLossBetweenDates(getBottleLossBetweenDates(sDate, fDate));
        or.setKegReturnsBetweenDates(getKegReturnsBetweenDates(sDate, fDate));
        or.setBottleReturnsBetweenDates(getBottleReturnsBetweenDates(sDate, fDate));
        
        //----------------------
        //--Calculation Part 5--
        //----------------------
        // Fill in the materials used.
        for (final MaltType type : MaltType.values()) {
                fermentableTotals.put(type, 0f);
        }
        or.setTotalHopsBetweenDates(getTotalHopsBetweenDates(sDate,fDate));
        
        or.setTotalBarleyBetweenDates(fermentableTotals.get(MaltType.BARLEY));
        or.setTotalWheatBetweenDates(fermentableTotals.get(MaltType.WHEAT));
        or.setTotalOatsBetweenDates(fermentableTotals.get(MaltType.OATS));
        or.setTotalHoneyBetweenDates(fermentableTotals.get(MaltType.HONEY) 
                + fermentableTotals.get(MaltType.MOLASSES));
        
        //----------------------
        //--Calculation Part 6--
        //----------------------
        or.setdistCoBtlSalesBetweenDates(getDistCoBottleSalesBetweenDates(sDate, fDate));
        or.setdistCoKegSalesBetweenDates(getDistCoKegSalesBetweenDates(sDate, fDate));
        
        return or;
    }
    
    protected EntityManager getEntityManager() {
        return em;
    }
}
